/**
 * @title 全局公共方法
 * @Author webxue
 * @Date 2022/12/01
 */

import { spawnSync } from "child_process";
import * as dayjs from "dayjs";
import { isIPv4 } from "net";
import { networkInterfaces } from "os";

export class UtilTool {

  /**
   * @title 树处理
   * @param {Array<any>} data - 原数组
   * @param {[string,string]} relation - 父子对应关系[子的字段名,父的字段名]
   * @return {Array} 树结构
   * */
  static makeTree (data: Array<any>, relation = ["id", "p_id"]) {
    const [sonField, parentField] = relation;

    // 删除 所有 children,以防止多次调用
    data.forEach(item => delete item.children);

    // 将数据存储为 以 id 为 KEY 的 map 索引数据列
    const map = {};
    data.forEach(item => map[item[sonField]] = item);

    const val = [];
    data.forEach(item => {
      // 以当前遍历项，的pid,去map对象中找到索引的id
      const parent = map[item[parentField]];
      // 好绕啊，如果找到索引，那么说明此项不在顶级当中,那么需要把此项添加到，他对应的父级中
      if (parent) {
        parent.children || (parent.children = []);
        parent.children.push(item);
      } else {
        //如果没有在map中找到对应的索引ID,那么直接把 当前的item添加到 val结果集中，作为顶级
        val.push(item);
      }
    });
    return val;
  }

  /**
   * @title 同步命令执行函数
   * @param {string} command 执行命令
   */
  static commandSync (command: string) {
    return new Promise((resolve, reject) => {
      const [progress, ...args] = command.split(" ");
      const result = spawnSync(progress, args, { stdio: "inherit" });
      if (result.status !== 0) return reject(result);
      resolve(result);
    });
  }

  /**
   * @title 分隔符转驼峰
   * @example user_name_age => userNameAge TransformVar("user_name_age","_",false,false)
   * @example user_name_age => UserNameAge TransformVar("user_name_age","_",false,true)
   * @example userNameAge => user_name_age TransformVar("userNameAge","_",true)
   * @param {string} str 源字符串
   * @param {string} split 分隔符
   * @param {boolean=false} reverse 是否驼峰转烤串(忽略第四个参数，首字母一定小写)
   * @param {boolean=false} firstUp 首字母是否大写
   * @return {string} 转换之后的字符串
   * */
  static transformVar (str: string = "", split: string = "_", reverse: boolean = false, firstUp: boolean = false) {
    // 烤串转驼峰
    if (!reverse) {
      const strArr = str.split(split);
      const newArr = strArr.map((field, index) => {
        if (firstUp) return `${field.charAt(0).toUpperCase()}${field.slice(1)}`;
        return `${index > 0 ? field.charAt(0).toUpperCase() : field.charAt(0)}${field.slice(1)}`;
      });
      return newArr.join("");
    }
    // 驼峰转烤串
    const strArr = str.match(/[a-zA-Z][a-z]+/g) || [];
    const newArr = strArr.map(str => str.toLowerCase());
    return newArr.join(split);
  }

  /**
   * @title 根据key-value找对象包对象属性
   * @example {A:{a:1,b:1},B:{a:2,b:2}} => 已知a=1 => ["A",{a:1,b:1}]
   * @param {object} origin - 源对象
   * @param {string} key - 对象key
   * @param {any} value - 对象value
   * @return {Array|Boolean} - 返回数组 ["A",{a:1,b:1}]
   * */
  static findObjByKeyVal (origin: object, key: string, value: any) {
    const result = Object.entries(origin).find(obj => obj[1][key] === value);
    if (result) return result;
    return false;
  }

  /**
   * @title 请求结果处理为小驼峰
   * @param {object} data - 响应结果
   * @return {object} 处理后的data
   * */
  static transformResponseData (data: object) {
    const result: { [key: string]: any } = {};
    for (const key in data) {
      const newkey: string = UtilTool.transformVar(key, "_", false, false);
      if (Array.isArray(data[key])) {
        result[newkey] = data[key].map(d => (String(d) === "[object Object]" ? UtilTool.transformResponseData(d) : d));
      } else if (String(data[key]) === "[object Object]") {
        result[newkey] = UtilTool.transformResponseData(data[key]);
      } else {
        result[newkey] = data[key];
      }
    }
    return result;
  }

  /**
   * js字符串中将?替换为匹配的值
   * @param {string} str - a ? b ? c ? d ?
   * @param {any[]} data - [1,2,3,4]
   * @return {string} a 1 b 2 c 3 d 4
   * */
  static transformStr (str: any, data: Array<any>) {
    let index = 0;
    str = str.split(" ");
    for (let i = 0; i < str.length; i++) {
      if (str[i] === "?") {
        str[i] = data[index];
        index++;
      }
    }
    return str.join(" ");
  }

  /**
   * 对一段方法进行耗时检测
   * @param {()=>void} callback - 回调函数
   * */
  static checkTime (callback) {
    return new Promise(async resolve => {
      const startTime = dayjs().valueOf();
      await callback();
      const endTime = dayjs().valueOf();
      resolve(endTime - startTime);
    });
  }

  /**
   * 数组转对象
   * @param {Array<string>>} arr - 原数组
   * @param {(prev,curr,index)=>[string,string]} keyValCb - 回调函数，接受三个参数，(上一项,当前项,当前下标) => [key字符串,value字符串]
   * @return {JSON}
   * @example ["a=1","b=2","c=3"] ===> {a:1,b:2,c:3}
   * [1,2,3] ===> {tagId0:1,tagId1:2,tagId2:3}
   * */
  static arrayToJson (arr: Array<string>, keyValCb: (prev, curr, index) => [string, string]) {
    return arr.reduce((acc, curr, index) => {
      const [key, value] = keyValCb(acc, curr, index);
      acc[key] = value;
      return acc;
    }, {});
  }

  /**
   * ipv6转ipv4
   * @param {string} ip - ipv6地址
   * @return {string} - ipv4地址
   * */
  static ipv6ToIpv4 (ip: string) {
    return isIPv4(ip) ? ip : ip.split(":").reverse()[0];
  }

  /**
   * 入参处理
   * @param {object} request - 驼峰式请求参数
   * @return {object} 烤串式请求参数
   * */
  static handleEnterParams (request: object) {
    const params: { [key: string]: any } = {};
    for (const key in request) {
      params[UtilTool.transformVar(key, "_", true)] = request[key];
    }
    return params;
  }

  /**
   * 获取服务地址
   * @returns {object} { host }
   */
  static getServerInfo () {
    const [INTERNET, WLAN] = Object.entries(networkInterfaces());
    const { address } = [...INTERNET[1], ...WLAN[1]].find((e) => e.family === "IPv4");
    return {
      host:address || "127.0.0.1",
    }
  }
}
